package com.seleniti.enpareja.controller.ui.view;

import java.util.ArrayList;
import java.util.Collection;

import org.ioc.commons.flowcontrol.actioncontroller.ActionController;
import org.ioc.commons.flowcontrol.common.Callback;
import org.ioc.commons.flowcontrol.common.impl.ReceiverAdapter;
import org.ioc.commons.flowcontrol.eventbus.EventBus;
import org.ioc.commons.flowcontrol.logincontroller.IsLoggedUserInfoConsumer;
import org.ioc.commons.flowcontrol.operationmanager.IsOperation;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.operationmanager.OperationManagerTrigger;
import org.ioc.commons.flowcontrol.operationmanager.impl.OperationHandlerFactory;
import org.ioc.commons.model.dataprovider.PagedDataProvider;
import org.ioc.commons.model.globaldata.GlobalDataController.Consumer;
import org.ioc.commons.model.globaldata.IsGlobalDataConsumer;
import org.ioc.commons.ui.features.HasInfoType.InfoType;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.common.domain.Page;
import com.seleniti.enpareja.controller.control.place.EnParejaPlace;
import com.seleniti.enpareja.controller.model.integration.database.domain.Couple;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleRequest.Status;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.service.CoupleService;
import com.seleniti.enpareja.controller.ui.base.EnParejaViewBasePresenter;
import com.seleniti.enpareja.controller.ui.view.GetCoupleView.Action;
import com.seleniti.enpareja.controller.ui.view.GetCoupleView.Event;
import com.seleniti.enpareja.controller.ui.view.GetCoupleView.Operation;

/**
 * Presenter for the SignUp view
 * 
 * @author Jesús Lunar Pérez
 * 
 */
public class GetCoupleViewPresenter extends
		EnParejaViewBasePresenter<GetCoupleView, GetCoupleView.Event, GetCoupleView.Operation, GetCoupleView.Action>
		implements GetCoupleView.Presenter, IsLoggedUserInfoConsumer<LoggedUserInfo>, IsGlobalDataConsumer<GlobalData> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(GetCoupleViewPresenter.class);

	private PagedDataProvider<CoupleRequest> pageDataProvider;

	private CoupleService coupleService;

	private Consumer<GlobalData> consumer;

	public GetCoupleViewPresenter(GetCoupleView display, EventBus<Event> eventbus,
			OperationManager<Operation> operationManager, ActionController<Action> actionController) {
		super(display, eventbus, operationManager, actionController);
		this.consumer = this.globalDataController.registerGlobalDataConsumer(this);
		this.consumer.requires(GlobalData.UserAgent);
	}

	@Override
	public void onInitDisplay() {
		super.onInitDisplay();
		this.coupleService = serviceManager.serviceFactory().coupleService();
		logger.entering("onInitDisplay");

		this.pageDataProvider = new PagedDataProvider<CoupleRequest>() {

			private long totalCount;

			@Override
			public long getTotalCount() {
				return this.totalCount;
			}

			@Override
			public long getMinIndexVisibleRange() {
				return 0;
			}

			@Override
			public int getMinFecthLength() {
				return 50;
			}

			@Override
			public <O extends Enum<? extends O> & IsOperation> void fetchData(
					final Callback<Collection<CoupleRequest>, Throwable> callback, long start, long length,
					O operation, OperationManagerTrigger<O> trigger, String... requiredProperties) {
				LoggedUserInfo lui = loginController.getLoggedUserInfo();
				if (lui != null && lui.getUserId() != null) {
					serviceManager.serviceFactory().coupleService()
							.getCoupleRequestsToOrFromUserPage(lui.getUserId(), start, length)
							.caller(GetCoupleViewPresenter.this).with("elements.from", "elements.to")
							.performing(Operation.GettingCoupleRequests, operationManager)
							.to(new ReceiverAdapter<Page<CoupleRequest>>() {

								@Override
								public void success(Page<CoupleRequest> result) {
									totalCount = result.getTotal();
									callback.onSuccess(result.getElements());
								}

								@Override
								public void failure(Throwable failure) {
									callback.onFailure(failure);
								}
							});
				} else {
					callback.onSuccess(new ArrayList<CoupleRequest>());
				}

			}
		};
		display.setPagedDataProvider(pageDataProvider);

		this.loginController.requiresLoggedUserInfoIfDisplayActive(this);

		logger.exiting("onInitDisplay");
	}

	@Override
	public void onActive() {
		super.onActive();

		logger.info("Activating Get Couple view");

		shell.nonAssociatedPanelVisibility().setVisible(false);
		shell.firstStepsVisibility().setVisible(false);
		shell.nonAssociatedButtonVisibility().setVisible(!shell.firstStepsVisibility().isVisible());
		shell.requestsToYouPanelVisibility().setVisible(false);
		shell.pendingRequestsFromYouPanelVisibility().setVisible(false);
		shell.rejectedRequestsFromYouPanelVisibility().setVisible(false);

		shell.refreshButtonVisibility().setVisible(true);

		refreshData();
		this.consumer.ensureRequirements();
	}

	@Override
	public void onRequiredGlobalDataReady() {
		showGoBackButtonIfRequired();
	}

	@Override
	public void onRequiredGlobalDataNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onRequiredGlobalDataUpdated(GlobalData globalDataUpdated) {
		showGoBackButtonIfRequired();
	}

	@Override
	protected void refreshData() {
		super.refreshData();

		display.startSearch();
	}

	protected void bindComponents() {
		super.bindComponents();
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(display));
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(shell.globalLoader()));

	}

	@Override
	public void onGetCoupleNow() {
		CoupleRequest selected = display.getSelectedCoupleRequest();

		serviceManager.serviceFactory().coupleService().makeCouple(selected)
				.performing(Operation.MakingCouple, operationManager).caller(this).to(new ReceiverAdapter<Couple>() {

					@Override
					public void success(Couple result) {
						logger.info("Refreshing logged user info...");
						loginController.refreshLoggedUserInfo();
						placeController.goTo(EnParejaPlace.Main);
					}
				});
	}

	@Override
	public void onSelectedCoupleRequestChanged() {
		refreshActionsFromSelected();
	}

	protected void refreshActionsFromSelected() {
		CoupleRequest selected = display.getSelectedCoupleRequest();
		LoggedUserInfo lui = this.loginController.getLoggedUserInfo();

		boolean pendingActions = selected != null && selected.getStatus() == Status.PENDING && selected.getTo() != null
				&& selected.getTo().getUserId() != null && lui != null
				&& selected.getTo().getUserId().equals(lui.getUserId()) && (lui.getCoupleId() == null || lui.getCoupleType()!=CoupleType.USERS_COUPLE);
		boolean stillPendingActions = selected != null && selected.getStatus() == Status.PENDING
				&& selected.getFrom() != null && selected.getFrom().getUserId() != null && lui != null
				&& selected.getFrom().getUserId().equals(lui.getUserId()) && (lui.getCoupleId() == null || lui.getCoupleType()!=CoupleType.USERS_COUPLE);
		boolean rejectedActions = selected != null && selected.getStatus() == Status.REJECTED
				&& selected.getFrom() != null && selected.getFrom().getUserId() != null && lui != null
				&& selected.getFrom().getUserId().equals(lui.getUserId()) && (lui.getCoupleId() == null || lui.getCoupleType()!=CoupleType.USERS_COUPLE);

		display.pendingActions().setVisible(pendingActions);
		display.stillPendingActions().setVisible(stillPendingActions);
		display.rejectedActions().setVisible(rejectedActions);

		boolean coupleGotInfo = lui != null && lui.getCoupleId() != null && !pendingActions && !stillPendingActions
				&& !rejectedActions;
		display.coupleGotInfo().setVisible(coupleGotInfo);

		if (lui != null && lui.getCoupleType() == CoupleType.SINGLE_USER) {
			display.coupleGotInfo().setType(InfoType.INFO);
			display.coupleGotInfo().setText(
					Format.substitute("Actualmente estás utilizando la aplicación en modo soltero."));
		} else if (lui != null && lui.getCoupleType() == CoupleType.TESTING_MODE_COUPLE) {
			display.coupleGotInfo().setType(InfoType.INFO);
			display.coupleGotInfo().setText(
					Format.substitute("Actualmente estás utilizando la aplicación en modo de prueba."));
		} else {
			display.coupleGotInfo().setType(InfoType.OPERATION_OK);
			display.coupleGotInfo().setText(
					Format.substitute("Enhorabuena. Ya estás emparejado{0} ☺.",
							lui != null && lui.getCoupleName() != null && !lui.getCoupleName().isEmpty() ? " con "
									+ lui.getCoupleName() : ""));
		}
	}

	@Override
	public void onDismissSelectedRequest() {
		CoupleRequest selected = display.getSelectedCoupleRequest();
		if (selected != null) {
			display.clearSelection();
			refreshActionsFromSelected();

			this.coupleService.dismissCoupleRequest(selected).caller(GetCoupleViewPresenter.this)
					.performing(Operation.DismissingCoupleRequest, operationManager).to(new ReceiverAdapter<Void>() {

						@Override
						public void success(Void result) {
							refreshData();
							loginController.refreshLoggedUserInfo();
						}
					});
		}
	}

	@Override
	public void onRejectSelectedRequest() {
		CoupleRequest selected = display.getSelectedCoupleRequest();
		if (selected != null) {
			display.clearSelection();
			refreshActionsFromSelected();

			this.coupleService.rejectCoupleRequest(selected).caller(GetCoupleViewPresenter.this)
					.performing(Operation.DismissingCoupleRequest, operationManager).to(new ReceiverAdapter<Void>() {

						@Override
						public void success(Void result) {
							loginController.refreshLoggedUserInfo();
							refreshData();
						}
					});
		}
	}

	@Override
	public void onLoggedUserInfoNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onLoggedUserInfoDataReady() {
		refreshActionsFromSelected();
	}

	@Override
	public void onLoggedUserInfoDataUpdated(LoggedUserInfo loggedUserInfo) {
		refreshActionsFromSelected();
	}

}
